#include "scene.h"
#include "model/cart_model.h"

const float Scene::day_light_color[4] = {1.0f, 1.0f, 1.0f, 0.0f};
const float Scene::light_position[4] = {10.0f, 10.0f, 8.0f,0.0f};
const float Scene::day_light_ambient[4] = {0.05f, 0.05f, 0.05f,0}; //1.0f};
const float Scene::night_light_color[4]={0.10f,0.11f,0.11f,0.0f};
const float Scene::night_light_ambient[4]={0,0,0,0};

//Luces auxiliares
const float Scene::night_light_pos_aux[4][4]={	{90,90,0,0},
												{-30,30,0,0},
												{-90,-90,0,0},
												{30,-30,0,0}};

const float Scene::night_light_color_aux[4][4] = {	{0, 0, 0.15f,1},
													{0, 0, 0.0f,1},
													{0, 0, 0.15f,1},
													{0, 0, 0.0f,1}};


Scene::Scene(){}

Scene::~Scene(){
	clean();
}

void Scene::clean(){
	for(ControllerList::iterator it=controllers.begin();it!=controllers.end();++it)
		delete (*it);
	controllers.clear();
}

void Scene::initLights(){
	glShadeModel (GL_SMOOTH);
	glEnable(GL_DEPTH_TEST);
	float ambiente[] = { 0.5f, 0.5f, 0.5f, 1.0f };
	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambiente);

	glLightfv(GL_LIGHT0, GL_DIFFUSE, day_light_color);
	glLightfv(GL_LIGHT0, GL_AMBIENT, day_light_ambient);
	glLightfv(GL_LIGHT0, GL_POSITION, light_position);

	glLightfv(GL_LIGHT1, GL_POSITION, light_position);
	glLightfv(GL_LIGHT1, GL_DIFFUSE, night_light_color);
	glLightfv(GL_LIGHT1, GL_AMBIENT, night_light_ambient);

	float noAmbient[] = {0.5f, 0.5f, 0.0f, 1.0f};       //low ambient light

	glLightfv(GL_LIGHT3, GL_POSITION, CartModel::lightPos);
	glLightfv(GL_LIGHT3, GL_AMBIENT, noAmbient);
	glLightfv(GL_LIGHT3, GL_DIFFUSE, CartModel::lightColor);
	glLightf(GL_LIGHT3, GL_SPOT_EXPONENT,15.0f);
	glLightf(GL_LIGHT3, GL_SPOT_CUTOFF, 20.0f);

	//Luces auxiliares
	for (GLint i=0;i<4;i++){
		glLightfv(GL_LIGHT4+i, GL_POSITION, night_light_pos_aux[i]);
		//glLightfv(GL_LIGHT4+i, GL_AMBIENT, night_light_color_aux[i]);
		glLightfv(GL_LIGHT4+i, GL_DIFFUSE, night_light_color_aux[i]);

		glEnable(GL_LIGHT4+i);
	}


	glEnable(GL_LIGHT1);

	glEnable(GL_LIGHTING);

	day=false;
}

void Scene::init(){
	DisplayListsUtils::instance().createDisplayLists();

	glClearColor (0.02, 0.02, 0.04, 0.0);
	initLights();

	Framework& f = Framework::instance();

	// Vista Top
	Rectangle viewport(	f.getWindowWidth()*3/4,
						f.getWindowWidth(),f.getWindowHeight()/2,f.getWindowHeight());

	TopModel* m1 = new TopModel();
	TopView* v1 = new TopView(viewport,DisplayListsUtils::instance().getAxis2DTopHandle(),*m1);

	Controller2D* c1 = new TopController(v1,m1);
	controllers.push_back(c1);

	// Vista Altura
	Rectangle viewport3(	f.getWindowWidth()*3/4,
							f.getWindowWidth(),0,f.getWindowHeight()/2);

	HeightModel* m3 = new HeightModel();

	HeightView* v3 = new HeightView(viewport3,DisplayListsUtils::instance().getAxis2DHeightHandle(),*m3);

	Controller2D* c3 = new HeightController(v3,m3);
	controllers.push_back(c3);

	// Vista Perspectiva
	Rectangle viewport2(	0.0f,f.getWindowWidth()*3/4,
							0.0f,f.getWindowHeight());

	CartModel cart(GL_LIGHT3);
	PerspectiveModel* pModel = new PerspectiveModel(*m3,*m1,cart);

	PerspectiveView* v2 = new PerspectiveView(	viewport2,
												DisplayListsUtils::instance().getAxisHandle(),
												DisplayListsUtils::instance().getGridHandle(),*pModel);
	Controller3D* c2 = new Controller3D(v2,pModel);
	controllers.push_back(c2);
}

void Scene::display(){
	glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	for(ControllerList::iterator it=controllers.begin();it!=controllers.end();++it)
		(*it)->display();
}

void Scene::keyboard(unsigned char key,int x ,int y){
	//Framework& f = Framework::instance();
	processKey(key,x,y);

	for(ControllerList::iterator it=controllers.begin();it!=controllers.end();++it)
		(*it)->keyboard(key,x,y);

	//f.redisplay();
}

void Scene::processKey(unsigned char key,int x ,int y){
	switch(key){
		case 0x1b:
			clean();
			exit(0);
			break;

		case 'r':
			reset();
			break;

		case 'i':
			swapLights();
			break;
	}
}

void Scene::swapLights(){
	if(day){
		glDisable(GL_LIGHT0);
		glEnable(GL_LIGHT1);
	}else{
		glDisable(GL_LIGHT1);
		glEnable(GL_LIGHT0);
	}

	day=!day;
}

void Scene::mousePressed(int key,int state,int x ,int y){
	Framework& f = Framework::instance();
	// Invierto y
	y =  (int)f.getWindowHeight()-y;

	Controller* controller = getViewportController(x,y);

	// El mouse dejo la ventana y no llego a llamarse
	// a entryFunc
	if(!controller)
		return;

	View& view = controller->getView();

	float newX = (float)(x-view.getViewport().x1)/view.getViewport().width();
	float newY = (float)(y-view.getViewport().y1)/view.getViewport().height();

	controller->mousePressed(key,state,newX,newY);

	for(ControllerList::iterator it=controllers.begin();it!=controllers.end();++it){
		if (*it != controller)
			(*it)->notifyViewportLeave();
	}

	//f.redisplay();
}

void Scene::mouseEntry(int state){
	// El mouse abandona todos los viewports, se les notifica
	//Framework& f = Framework::instance();
	if(state==GLUT_LEFT){
		for(ControllerList::iterator it=controllers.begin();it!=controllers.end();++it)
			(*it)->notifyViewportLeave();
	}
	//f.redisplay();
}

void Scene::mouseMotion(int x ,int y){
	Framework& f = Framework::instance();
	// Invierto y
	y =  (int)f.getWindowHeight()-y;
	Controller* controller = getViewportController(x,y);

	// El mouse dejo la ventana y no llego a llamarse
	// a entryFunc
	if(!controller)
		return;

	View& view = controller->getView();

	float newX = ((float)x-view.getViewport().x1)/view.getViewport().width();
	float newY = ((float)y-view.getViewport().y1)/view.getViewport().height();

	controller->mouseMotion(newX,newY);

	//f.redisplay();
}

void Scene::reshape(int oldW,int oldH,int w ,int h){
	float ex = (float)w/(float)oldW;
	float ey = (float)h/(float)oldH;

	for(ControllerList::iterator it=controllers.begin();it!=controllers.end();++it)
		(*it)->reshape(ex,ey);
}

Controller* Scene::getViewportController(int x, int y){
	for(ControllerList::iterator it=controllers.begin();it!=controllers.end();++it){
		if((*it)->getView().getViewport().intersect(x,y))
			return (*it);
	}
	return NULL;
}

void Scene::timer(int value){
	processTimer(value);
	for(ControllerList::iterator it=controllers.begin();it!=controllers.end();++it)
		(*it)->timer(value);

}

void Scene::processTimer(int value){
	Framework& f = Framework::instance();
	f.redisplay();
}

void Scene::reset(){
	clean();
	init();
}
